package com.lt.common;

import android.content.res.Resources;
import android.os.Environment;
import android.text.TextUtils;

import com.lt.LibConstant;
import com.lt.base.BaseApplication;
import com.lt.exception.CustomException;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 文件处理
 *
 * @author luotian
 */
public class FileHelper {
    private static FileHelper instance;

    private FileHelper() {
    }


    public static FileHelper getInstance() {
        if (instance == null) {
            synchronized (FileHelper.class) {
                instance = new FileHelper();
            }
        }
        return instance;
    }

    //-------------------------------------------------------


    /**
     * @return 是否存在sd卡
     */
    public boolean isHaveStorage() {
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }

    /**
     * 获取SD卡缓存目录
     *
     * @return 缓存目录文件夹 或 null（无SD卡或SD卡挂载失败）
     */
    public File getExternalCacheDirectory() {
        File appCacheDir = null;
        if (isHaveStorage() || !Environment.isExternalStorageRemovable()) {
            appCacheDir = BaseApplication.getContext().getExternalCacheDir();
            if (appCacheDir == null) {// 有些手机需要通过自定义目录
                appCacheDir = new File(Environment.getExternalStorageDirectory(), "Android/data/" + BaseApplication.getContext().getPackageName() + "/cache");
            }
            if (!appCacheDir.exists() && !appCacheDir.mkdirs()) {
                DebugHelper.LogLine("getExternalDirectory fail ,the reason is make directory fail !");
            }
        }
        return appCacheDir;
    }

    public File getSDPath() {
        return isHaveStorage() ? Environment.getExternalStorageDirectory() : null;
    }


    /**
     * 获取内存缓存目录
     *
     * @return 缓存目录文件夹 或 null（创建目录文件失败）
     * 注：该方法获取的目录是能供当前应用自己使用，外部应用没有读写权限，如 系统相机应用
     */
    public File getInternalCacheDirectory() {
        File appCacheDir = BaseApplication.getContext().getCacheDir();// /data/data/app_package_name/cache
        if (!appCacheDir.exists() && !appCacheDir.mkdirs()) {
            DebugHelper.LogLine("getInternalDirectory fail ,the reason is make directory fail !");
        }
        return appCacheDir;
    }


    /**
     * 获取应用专属缓存目录
     * android 4.4及以上系统不需要申请SD卡读写权限
     * 因此也不用考虑6.0系统动态申请SD卡读写权限问题，切随应用被卸载后自动清空 不会污染用户存储空间
     *
     * @return 缓存文件夹 如果没有SD卡或SD卡有问题则返回内存缓存目录，否则优先返回SD卡缓存目录
     */
    public File getCacheDir() {
        File appCacheDir = getExternalCacheDirectory();
        if (appCacheDir == null || !appCacheDir.exists()) {
            appCacheDir = getInternalCacheDirectory();
        }
        if (appCacheDir != null) {
            if (!appCacheDir.exists() && !appCacheDir.mkdirs()) {
                DebugHelper.LogLine("getCacheDirectory fail ,the reason is make directory fail !");
            }
        }
        return appCacheDir;
    }


    /**
     * get file name from path, not include suffix
     * <p>
     * <pre>
     *      getFileNameWithoutExtension(null)               =   null
     *      getFileNameWithoutExtension("")                 =   ""
     *      getFileNameWithoutExtension("   ")              =   "   "
     *      getFileNameWithoutExtension("abc")              =   "abc"
     *      getFileNameWithoutExtension("a.mp3")            =   "a"
     *      getFileNameWithoutExtension("a.b.rmvb")         =   "a.b"
     *      getFileNameWithoutExtension("c:\\")              =   ""
     *      getFileNameWithoutExtension("c:\\a")             =   "a"
     *      getFileNameWithoutExtension("c:\\a.b")           =   "a"
     *      getFileNameWithoutExtension("c:a.txt\\a")        =   "a"
     *      getFileNameWithoutExtension("/home/admin")      =   "admin"
     *      getFileNameWithoutExtension("/home/admin/a.txt/b.mp3")  =   "b"
     * </pre>
     *
     * @return file name from path, not include suffix
     */
    public String getFileNameWithoutExtension(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return filePath;
        }

        int extenPosi = filePath.lastIndexOf(LibConstant.FILE_EXTENSION_SEPARATOR);
        int filePos = filePath.lastIndexOf(File.separator);
        if (filePos == -1) {
            return (extenPosi == -1 ? filePath : filePath.substring(0, extenPosi));
        }
        if (extenPosi == -1) {
            return filePath.substring(filePos + 1);
        }
        return (filePos < extenPosi ? filePath.substring(filePos + 1, extenPosi) : filePath
                .substring(filePos + 1));
    }


    /**
     * get suffix of file from path
     * <p>
     * <pre>
     *      getFileExtension(null)               =   ""
     *      getFileExtension("")                 =   ""
     *      getFileExtension("   ")              =   "   "
     *      getFileExtension("a.mp3")            =   "mp3"
     *      getFileExtension("a.b.rmvb")         =   "rmvb"
     *      getFileExtension("abc")              =   ""
     *      getFileExtension("c:\\")              =   ""
     *      getFileExtension("c:\\a")             =   ""
     *      getFileExtension("c:\\a.b")           =   "b"
     *      getFileExtension("c:a.txt\\a")        =   ""
     *      getFileExtension("/home/admin")      =   ""
     *      getFileExtension("/home/admin/a.txt/b")  =   ""
     *      getFileExtension("/home/admin/a.txt/b.mp3")  =   "mp3"
     * </pre>
     *
     * @return 文件的扩展名
     */
    public String getFileExtension(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return filePath;
        }

        int extenPosi = filePath.lastIndexOf(".");
        int filePos = filePath.lastIndexOf(File.separator);
        if (extenPosi == -1) {
            return "";
        }
        return (filePos >= extenPosi) ? "" : filePath.substring(extenPosi + 1);
    }

    /**
     * 文件转为流
     *
     * @param file 文件
     * @return {@link FileInputStream}
     */
    public FileInputStream toStream(File file) {
        try {
            return new FileInputStream(file);
        } catch (FileNotFoundException e) {
            throw new CustomException(e);
        }
    }

    /**
     * 获得输入流
     *
     * @param file 文件
     * @return 输入流
     * @throws CustomException 文件未找到
     */
    public BufferedInputStream getInputStream(File file) throws CustomException {
        return new BufferedInputStream(toStream(file));
    }

    /**
     * 获取文件的大小
     * file.length()方法，类型long Byte。支持大文件（9223372036854775807），文件大小不准。
     *
     * @param file 文件
     * @return 文件大小
     */
    public long getFileSize(File file) {
        return (file.exists() && file.isFile() ? file.length() : 0);
    }

    /**
     * 获取文件的大小
     * file.length()方法，类型long Byte。支持大文件（9223372036854775807），文件大小不准。
     *
     * @param file 文件
     * @return 文件大小
     */
    public long getFileSizeB(File file) {
        long size = 0;
        if (file.exists()) {
            try {
                FileInputStream fis = new FileInputStream(file);
                size = fis.available();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return size;
    }

    /**
     * @param filePath 文件路径
     * @return 返回文件夹路径
     */
    public String getFolderPath(String filePath) {

        if (TextUtils.isEmpty(filePath)) {
            return filePath;
        }

        int filePos = filePath.lastIndexOf(File.separator);
        return (filePos == -1) ? "" : filePath.substring(0, filePos);
    }

    /**
     * @return 文件名
     */
    public String getFileName(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return filePath;
        }

        int filePos = filePath.lastIndexOf(File.separator);
        return (filePos == -1) ? filePath : filePath.substring(filePos + 1);
    }


    public boolean makeDirs(String path) {
        if (TextUtils.isEmpty(path)) {
            return false;
        }
        File file = new File(path);
        if (!file.isDirectory()) {
            file = new File(getFolderPath(path));
        }
        return (file.exists() && file.isDirectory()) || file.mkdirs();
    }

    public boolean makeDirs(File dir) {
        if (dir == null || dir.exists()) {
            return false;
        }
        return dir.mkdir();
    }

    /**
     * 创建文件
     */
    public boolean createFile(File file) {
        if (!file.exists()) {
            try {
                Objects.requireNonNull(file.getParentFile()).mkdirs();
                return file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /**
     * @param file   要写的文件
     * @param stream 写入的流
     * @param append 是否是文件结尾追加流
     * @return 是否成功
     */
    public boolean writeFile(File file, InputStream stream, boolean append) throws CustomException {
        OutputStream o = null;
        try {
            createFile(file);
            o = new FileOutputStream(file, append);
            byte[] data = new byte[1024];
            int length = -1;
            while ((length = stream.read(data)) != -1) {
                o.write(data, 0, length);
            }
            o.flush();
            return true;
        } catch (FileNotFoundException e) {
            throw new CustomException("FileNotFoundException occurred. ", e);
        } catch (IOException e) {
            throw new CustomException("IOException occurred. ", e);
        } finally {
            if (o != null) {
                try {
                    o.close();
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 写文件
     */
    public boolean writeFile(String filePath, InputStream stream, boolean append) throws CustomException {
        return writeFile(filePath != null ? new File(filePath) : null, stream, append);
    }

    /**
     * 写文件
     */
    public boolean writeFile(File file, InputStream stream) throws CustomException {
        return writeFile(file, stream, false);
    }

    public boolean writeFile(File file, String content, boolean append) throws CustomException {
        if (TextUtils.isEmpty(content)) {
            return false;
        }
        FileWriter fileWriter = null;
        try {
            if (!createFile(file))
                return false;
            fileWriter = new FileWriter(file, append);
            fileWriter.write(content);
            fileWriter.close();
            return true;
        } catch (IOException e) {
            throw new RuntimeException("IOException occurred. ", e);
        } finally {
            if (fileWriter != null) {
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public boolean writeFile(File file, String content) {
        return writeFile(file, content, false);
    }


    /**
     * 将byte[]写到文件
     */
    public File writeFile(byte[] data, File file) {
        try {
            FileOutputStream outputStream = new FileOutputStream(file); // 文件输出流
            outputStream.write(data); // 写入sd卡中
            outputStream.close(); // 关闭输出流
        } catch (Exception e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * @param file   保存的文件
     * @param base64 视频文件的base64
     * @return 是否成功
     */
    public boolean base64ToVideo(File file, String base64) {
        try {
            byte[] videoBytes = android.util.Base64.decode(base64.getBytes(), android.util.Base64.DEFAULT);
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(videoBytes);
            fos.flush();
            fos.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * @return 读取文本文件中的内容
     */
    public String readFile2String(File file) throws CustomException {
        String content = ""; // 文件内容字符串
        if (!file.isFile()) {
            return content;
        }
        try {
            InputStream is = new FileInputStream(file);
            InputStreamReader inputreader = new InputStreamReader(is);
            BufferedReader buffreader = new BufferedReader(inputreader);
            String line;
            // 分行读取
            while ((line = buffreader.readLine()) != null) {
                content += line + "\n";
            }
            is.close();
        } catch (FileNotFoundException e) {
            throw new CustomException(e);
        } catch (IOException e) {
            throw new CustomException(e);
        }
        return content;
    }


    public static String readInputStream2Str(InputStream inputStream, String charsetName) throws CustomException {
        InputStreamReader inputStreamReader = null;
        try {
            inputStreamReader = new InputStreamReader(inputStream, charsetName);
        } catch (UnsupportedEncodingException e) {
            throw new CustomException(e);
        }
        BufferedReader reader = new BufferedReader(inputStreamReader);
        StringBuffer sb = new StringBuffer();
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line);
                sb.append("\n");
            }
        } catch (IOException e) {
            throw new CustomException(e);
        }
        return sb.toString();
    }

    public String readFile2Str(File file) throws CustomException {
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            return readInputStream2Str(fileInputStream, LibConstant.UTF_8);
        } catch (FileNotFoundException e) {
            throw new CustomException(e);
        }
    }

    public List<String> readFileToList(File file, String charsetName) throws CustomException {
        List<String> fileContent = new ArrayList<>();
        if (file == null || !file.exists() || !file.isFile()) {
            throw new CustomException("File not found");
        }
        BufferedReader reader = null;
        try {
            InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
            reader = new BufferedReader(is);
            String line;
            while ((line = reader.readLine()) != null) {
                fileContent.add(line);
            }
            reader.close();
            return fileContent;
        } catch (IOException e) {
            throw new CustomException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public byte[] readFileByte(File file) throws CustomException {
        // 获得输入流
        FileInputStream inStream;
        try {
            inStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            throw new CustomException(e);
        }
        // new一个缓冲区
        byte[] buffer = new byte[1024];
        int len = 0;
        // 使用ByteArrayOutputStream类来处理输出流
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        try {
            while ((len = inStream.read(buffer)) != -1) {
                // 写入数据
                outStream.write(buffer, 0, len);
            }
            // 得到文件的二进制数据
            byte[] data = outStream.toByteArray();
            // 关闭流
            outStream.close();
            inStream.close();
            return data;
        } catch (IOException e) {
            throw new CustomException(e);
        }
    }

    public String readAssert(String fileName) throws CustomException {
        try {
            InputStream inputStream = BaseApplication.getContext().getResources().getAssets().open(fileName);
            return readInputStream2Str(inputStream, LibConstant.UTF_8);
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }

    public byte[] readAssert2Bytes(String fileName) throws CustomException {
        try {
            InputStream is = BaseApplication.getContext().getAssets().open(fileName);
            int size = is.available();
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();
            return buffer;
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }

    public List<String> readFileFromRaw(int rawId, String charsetName) throws CustomException {
        Resources res = BaseApplication.getContext().getResources();
        List<String> fileContent = new ArrayList<>();
        BufferedReader reader = null;
        try {
            InputStreamReader is = new InputStreamReader(res.openRawResource(rawId), charsetName);
            reader = new BufferedReader(is);
            String line;
            while ((line = reader.readLine()) != null) {
                fileContent.add(line);
            }
            reader.close();
            return fileContent;
        } catch (IOException e) {
            throw new CustomException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public List<String> readFileFromRaw(int rawId) {
        return readFileFromRaw(rawId, LibConstant.UTF_8);
    }

    public boolean rename(String oldPath, String newPath) {
        File oldFile = new File(oldPath);
        File newFile = new File(newPath);
        if (oldFile.exists()) {
            return oldFile.renameTo(newFile);
        }
        return false;
    }


    public void copyFile(File src, File dst) throws IOException {
        if (!dst.exists()) {
            dst.createNewFile();
        }
        FileChannel inChannel = new FileInputStream(src).getChannel();
        FileChannel outChannel = new FileOutputStream(dst).getChannel();
        try {
            inChannel.transferTo(0, inChannel.size(), outChannel);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inChannel != null)
                inChannel.close();
            if (outChannel != null)
                outChannel.close();
        }
    }

    /**
     * 删除文件
     *
     * @param file 文件
     * @return {@code true}: 删除成功<br>{@code false}: 删除失败
     */
    public boolean deleteFile(File file) {
        return file != null && (!file.exists() || file.isFile() && file.delete());
    }

    /**
     * 删除目录
     *
     * @param dir 目录
     * @return {@code true}: 删除成功<br>{@code false}: 删除失败
     */
    public boolean deleteDir(File dir) {
        if (dir == null)
            return false;
        // 目录不存在返回true
        if (!dir.exists())
            return true;
        // 不是目录返回false
        if (!dir.isDirectory())
            return false;
        // 现在文件存在且是文件夹
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (file.isFile()) {
                    if (!deleteFile(file))
                        return false;
                } else if (file.isDirectory()) {
                    if (!deleteDir(file))
                        return false;
                }
            }
        }
        return dir.delete();
    }

    /**
     * 删除目录下的所有文件
     *
     * @param dir 目录
     * @return {@code true}: 删除成功<br>{@code false}: 删除失败
     */
    public boolean deleteFilesInDir(File dir) {
        if (dir == null)
            return false;
        // 目录不存在返回true
        if (!dir.exists())
            return true;
        // 不是目录返回false
        if (!dir.isDirectory())
            return false;
        // 现在文件存在且是文件夹
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (file.isFile()) {
                    if (!deleteFile(file))
                        return false;
                } else if (file.isDirectory()) {
                    if (!deleteDir(file))
                        return false;
                }
            }
        }
        return true;
    }

    public void moveFile(File srcFile, File destFile) {
        boolean rename = srcFile.renameTo(destFile);
        if (!rename) {
            try {
                copyFile(srcFile, destFile);
                deleteFile(srcFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
